﻿namespace DCHealthCheck.IntegrationTests {

    #region Usings
    using Contoso.ActiveDirectory;
    using DCHealthCheck;
    using DCHealthCheck.Common;
    using DCHealthCheck.DomainModel;
    using Microsoft.GroupPolicy;
    using Microsoft.Win32;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Data;
    using System.Data.Entity;
    using System.Data.Objects.DataClasses;
    using System.Data.SqlClient;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Management;
    using System.Net;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Threading; 
    #endregion

    /// <summary>
    /// These are functional tests that may require elevated permissions and RPC connectivity to the target DC used for testing.
    /// Some tests such as TestTimeSyncBuilder may fail if using a smart-card authenticated session.
    /// </summary>
    [TestClass]
    public class Tests {

        #region Members
        private static string DCHealthCheckTagRDN;

        private static log4net.ILog Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private static string TestDCName;

        private static readonly TimeSpan WmiSearcherTimeout = new TimeSpan(0, 2, 0); 
        #endregion

        #region Constructor
        static Tests() {
            if (ConfigurationManager.AppSettings["TestDCName"] != null) {
                TestDCName = ConfigurationManager.AppSettings["TestDCName"].ToString();
            }
            else {
                TestDCName = Environment.GetEnvironmentVariable("DCHealthCheckTestDCName");
            }

            if (ConfigurationManager.AppSettings["DCHealthCheckTagRDN"] != null) {
                DCHealthCheckTagRDN = ConfigurationManager.AppSettings["DCHealthCheckTagRDN"].ToString();
            }
            if (string.IsNullOrWhiteSpace(DCHealthCheckTagRDN)) {
                DCHealthCheckTagRDN = "CN=DCHealthCheckTag,CN=Users";
            }
        } 
        #endregion

        #region Private methods
        private static System.DirectoryServices.ActiveDirectory.DomainController GetADDomainControllerForTest(string dnsName) {
            System.DirectoryServices.ActiveDirectory.DirectoryContext dcDirectoryContext =
                new System.DirectoryServices.ActiveDirectory.DirectoryContext(
                    System.DirectoryServices.ActiveDirectory.DirectoryContextType.DirectoryServer,
                    dnsName);

            System.DirectoryServices.ActiveDirectory.DomainController dc =
                System.DirectoryServices.ActiveDirectory.DomainController.GetDomainController(dcDirectoryContext);

            Assert.IsNotNull(dc, "System.DirectoryServices.ActiveDirectory.DomainController dc is null.");

            return dc;
        }

        private static SiteProperties GetADSiteForTest(System.DirectoryServices.ActiveDirectory.DomainController dc) {
            ADDataAccess.Initialize();
            Assert.IsTrue(ADDataAccess.Initialized, "ADDataAccess.Initialized is false.");

            Assert.IsNotNull(dc, "System.DirectoryServices.ActiveDirectory.DomainController dc is null.");

            SiteProperties adSite = null;

            var adSites = ADDataAccess.GetADObjectProperties<SiteProperties>(
                ADDataAccess.ForestRootConfigurationNamingContext, null, string.Format("name={0}", dc.SiteName),
                PartitionScope.LocalPartition);

            adSite = adSites.Single();

            return adSite;
        }

        private static SiteServer GetADSiteServerForTest(System.DirectoryServices.ActiveDirectory.DomainController dc) {
            ADDataAccess.Initialize();
            Assert.IsTrue(ADDataAccess.Initialized, "ADDataAccess.Initialized is false.");

            Assert.IsNotNull(dc, "System.DirectoryServices.ActiveDirectory.DomainController dc is null.");

            var adSite = GetADSiteForTest(dc);
            Assert.IsNotNull(adSite, "adSite is null");

            var siteServer = adSite.SiteServers
                .Where(x => string.Equals(x.DnsHostName, dc.Name, StringComparison.OrdinalIgnoreCase))
                .FirstOrDefault();

            return siteServer;
        }

        private static ComputerProperties GetDCComputerAccountForTest(string dnsName) {
            if (string.IsNullOrWhiteSpace(dnsName)) {
                throw new ArgumentNullException("dnsName");
            }
            ADDataAccess.Initialize();
            Assert.IsTrue(ADDataAccess.Initialized, "ADDataAccess.Initialized = false");

            ComputerProperties testDcComputerAccount = null;

            foreach (var domainInfo in ADDataAccess.ADDomainInfo) {
                var dcComputerAccounts = ADDataAccess.GetADObjectProperties<ComputerProperties>(
                    domainInfo.DistinguishedName, null, ADDataAccess.QueryFilters.DomainControllers, PartitionScope.LocalPartition);

                testDcComputerAccount = dcComputerAccounts
                    .Where(x => x.DnsHostName.StartsWith(dnsName, StringComparison.CurrentCultureIgnoreCase))
                    .FirstOrDefault();

                if (testDcComputerAccount != null) break;
            }

            Assert.IsNotNull(testDcComputerAccount, "ADDataAccess.ComputerProperties testDcComputerAccount is null.");

            return testDcComputerAccount;
        }

        private static DomainController GetDomainController(ComputerProperties dcComputerAccount) {
            var domainController = new DomainController();

            domainController.Id = dcComputerAccount.ObjectGuid;
            domainController.ADSite = "FakeADSiteName";
            domainController.DN = dcComputerAccount.DN;
            domainController.DNSDomain = dcComputerAccount.DNSDomain;
            domainController.DNSName = dcComputerAccount.DnsHostName;
            domainController.NetbiosDomain = dcComputerAccount.Domain;
            domainController.OSName = dcComputerAccount.OperatingSystem;
            domainController.OSVersion = dcComputerAccount.OperatingSystemVersionFriendly;
            domainController.SamAccountName = dcComputerAccount.SamAccountName;
            domainController.WhenCreated = dcComputerAccount.WhenCreated;

            return domainController;
        }

        private static void TestGetMostRecentHealthCheckResult() {
            using (var entityManager = new EntityManager()) {
                var mostRecentResult = entityManager.QuerySets.SystemInfosSlim
                    .Select(x => x.CheckDateTimeUTC)
                    .OrderByDescending(x => x)
                    .FirstOrDefault();

                Debug.WriteLine(mostRecentResult.ToString());

            }
        }

        private static void TestGetW32TimeRegisrySettings() {
            // Get registry values for W32Time MaxNegPhaseCorrection and MaxPosPhaseCorrection
            // These may be specied as a group policy or service parameter.  Check the GPO setting first.
            object maxNegPhaseCorrectionValue = null;
            object maxPosPhaseCorrectionValue = null;
            uint maxNegPhaseCorrectionActualValue = 0;
            uint maxPosPhaseCorrectionActualValue = 0;

            uint maxNegPhaseCorrectionExpectedValue = 172800;
            uint maxPosPhaseCorrectionExpectedValue = 172800;

            using (RegistryKey registryKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, TestDCName))
            using (RegistryKey w32TimeGpoRegistryKey = registryKey.OpenSubKey(@"Software\Policies\Microsoft\W32Time\Config")) {
                if (w32TimeGpoRegistryKey != null) {
                    maxNegPhaseCorrectionValue = w32TimeGpoRegistryKey.GetValue("MaxNegPhaseCorrection");
                    if (maxNegPhaseCorrectionValue != null) {
                        if (uint.TryParse(maxNegPhaseCorrectionValue.ToString(), out maxNegPhaseCorrectionActualValue)) {
                            if (maxNegPhaseCorrectionActualValue != maxNegPhaseCorrectionExpectedValue) {
                                //this.ErrorMessage.AppendLine(string.Format(@" -Warning: W32Time service registry value for setting MaxNegPhaseCorrection: {0} does not match expected value: {1} Registry key: HKLM\Software\Policies\Microsoft\W32Time\Config",
                                //    maxNegPhaseCorrectionActualValue, maxNegPhaseCorrectionExpectedValue));
                            }
                        }
                    }

                    maxPosPhaseCorrectionValue = w32TimeGpoRegistryKey.GetValue("MaxPosPhaseCorrection");
                    if (maxPosPhaseCorrectionValue != null) {
                        if (uint.TryParse(maxPosPhaseCorrectionValue.ToString(), out maxPosPhaseCorrectionActualValue)) {
                            if (maxPosPhaseCorrectionActualValue != maxPosPhaseCorrectionExpectedValue) {
                                //this.ErrorMessage.AppendLine(string.Format(@" -Warning: W32Time service registry value for setting MaxPosPhaseCorrection: {0} does not match expected value: {1} Registry key: HKLM\Software\Policies\Microsoft\W32Time\Config",
                                //    maxPosPhaseCorrectionActualValue, maxPosPhaseCorrectionExpectedValue));
                            }
                        }
                    }
                }
                else {
                    // check the service configuration
                    using (RegistryKey w32TimeRegistryKey = registryKey.OpenSubKey(@"SYSTEM\CurrentControlSet\Services\W32Time\Config")) {
                        maxNegPhaseCorrectionValue = w32TimeGpoRegistryKey.GetValue("MaxNegPhaseCorrection");
                        if (maxNegPhaseCorrectionValue != null) {
                            if (uint.TryParse(maxNegPhaseCorrectionValue.ToString(), out maxNegPhaseCorrectionActualValue)) {
                                if (maxNegPhaseCorrectionActualValue != maxNegPhaseCorrectionExpectedValue) {
                                    //this.ErrorMessage.AppendLine(string.Format(@" -Warning: W32Time service registry value for setting MaxNegPhaseCorrection: {0} does not match expected value: {1} Registry key: HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config",
                                    //    maxNegPhaseCorrectionActualValue, maxNegPhaseCorrectionExpectedValue));
                                }
                            }
                        }

                        maxPosPhaseCorrectionValue = w32TimeGpoRegistryKey.GetValue("MaxPosPhaseCorrection");
                        if (maxPosPhaseCorrectionValue != null) {
                            if (uint.TryParse(maxPosPhaseCorrectionValue.ToString(), out maxPosPhaseCorrectionActualValue)) {
                                if (maxPosPhaseCorrectionActualValue != maxPosPhaseCorrectionExpectedValue) {
                                    //this.ErrorMessage.AppendLine(string.Format(@" -Warning: W32Time service registry value for setting MaxPosPhaseCorrection: {0} does not match expected value: {1} Registry key: HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config",
                                    //    maxPosPhaseCorrectionActualValue, maxPosPhaseCorrectionExpectedValue));
                                }
                            }
                        }
                    }
                }
            } // using (RegistryKey w32TimeGpoRegistryKey = registryKey.OpenSubKey(@"Software\Policies\Microsoft\W32Time\Config"))
        }

        private static void UpdateProcessInfoExecutable() {
            List<ProcessInfo> processInfos = new List<ProcessInfo>();

            using (var entityManager = new EntityManager()) {
                do {
                    processInfos.Clear();
                    processInfos = entityManager.Set<ProcessInfo>()
                        .Where(x => x.Executable == null)
                        .Take(1000)
                        //.Include(x => x.SystemInfo)
                        .ToList();

                    for (int index = 0; index < processInfos.Count; index++) {
                        var processInfo = processInfos[index];
                        if (!string.IsNullOrWhiteSpace(processInfo.ExecutablePath) && string.IsNullOrWhiteSpace(processInfo.Executable)) {
                            if (!processInfo.ExecutablePath.Any(x => Path.GetInvalidPathChars().Contains(x))) {
                                processInfo.Executable = Path.GetFileName(processInfo.ExecutablePath);
                            }
                        }

                        if (processInfo.Executable == null) {
                            processInfo.Executable = string.Empty;
                        }
                    }

                    entityManager.SaveChanges(true);
                } while (processInfos.Count > 0);
            }
        } 
        #endregion

        #region TestFixtureSetUp
        [TestInitialize]
        public void Test_Initialize() {
            Console.WriteLine(DateTime.Now);
            Utility.FlushDNSCache();
            ADDataAccess.Initialize();
            Console.WriteLine("{0} finished.", DateTime.Now);
        } 
        #endregion

        #region Primitives
        [TestMethod]
        [TestCategory("Primitives")]
        public void TestServiceErrorControlDescriptionEnum() {
            string enumName = Enum.GetName(typeof(ServiceErrorControl), Enum.Parse(typeof(ServiceErrorControl), "Ignore", true));
            Assert.IsTrue(enumName == "Ignore");
            Console.WriteLine("{0} finished", Utility.CurrentMethodName());
        }
        [TestMethod]
        [TestCategory("Primitives")]
        public void TestWinErrorEnum() {
            string enumName = Enum.GetName(typeof(WINERROR), ((WINERROR)9001));
            Assert.IsTrue(enumName == "DNS_ERROR_RCODE_FORMAT_ERROR");
            Console.WriteLine("{0} finished", Utility.CurrentMethodName());
        } 
        #endregion

        #region Connectivity
        [TestMethod]
        [TestCategory("Connectivity")]
        public void TestLdap() {
            var success = Utility.LdapTest(TestDCName, 389);
            Console.WriteLine("LDAP 389 test sucessful: {0}", success);
            Assert.IsTrue(success, "LDAP 389 test failed.");

            success = Utility.LdapTest(TestDCName, 3268);
            Console.WriteLine("GC 3268 test successful: {0}", success);
            Assert.IsTrue(success, "GC 3268 test failed.");

            /* the SSL port tests may fail if used on an interactive session where smart card certificates exist.
            success = Utility.LdapTest(testDCName, 636);
            Console.WriteLine("LDAP SSL 636 test successful: {0}", success);
            Assert.IsTrue(success, "LDAP SSL 636 test failed.");

            success = Utility.LdapTest(testDCName, 3269);
            Console.WriteLine("GC SSL 3269 test successful: {0}", success);
            Assert.IsTrue(success, "GC SSL 3269 test failed.");
            */
        }

        [TestMethod]
        [TestCategory("Connectivity")]
        public void TestNtpClient() {
            NTPClient ntpclient = new NTPClient(TestDCName);
            ntpclient.Connect(20);

            Assert.IsTrue(ntpclient.IsResponseValid);

            Console.WriteLine(ntpclient.ToString());
            Console.WriteLine("{0} finished", Utility.CurrentMethodName());
        }

        [TestMethod]
        [TestCategory("Connectivity")]
        public void TestPing() {
            PingResult pingResult = Utility.PingHost(TestDCName);
            Console.WriteLine("Round Trip Time (ms): {0}", pingResult.RoundTripTimeMs);
            Console.WriteLine("Percent Loss: {0}", pingResult.PercentLoss);

            Assert.IsTrue(pingResult.PercentLoss > -1);
            Assert.IsTrue(pingResult.RoundTripTimeMs > -1);
            Console.WriteLine("{0} finished", Utility.CurrentMethodName());
        } 
        #endregion

        #region TestRunner Initialization
        [TestMethod]
        [TestCategory("TestRunner Initialization")]
        public void TestGetADSites() {
            Dictionary<string, SiteProperties> adSites = StartupDataGenerator.GetADSites();
            Assert.IsTrue(adSites.Count > 0);

            Console.WriteLine("Sites found: {0}", adSites.Count);

            foreach (var adSite in adSites.Values) {
                Console.WriteLine("Site: {0}", adSite.ToFormattedString());

                foreach (var siteServer in adSite.SiteServers) {
                    if (siteServer.NTDSSettings == null) {
                        Log.WarnFormat("  Server: {0} does not have an NTDSSettings object", siteServer.DnsHostName);
                        continue;
                    }

                    Console.WriteLine(" Server: {0}", siteServer.ToFormattedString());
                    Console.WriteLine(" Connections:");

                    foreach (var connection in siteServer.NTDSSettings.NTDSConnections) {
                        Console.WriteLine(connection.ToFormattedString());
                    }
                }
            }

            Console.WriteLine("{0} finished", Utility.CurrentMethodName());
        } 
        [TestMethod]
        [TestCategory("TestRunner Initialization")]
        public void TestDBContext() {
            using (var context = new DCHealthCheck.DomainModel.DCHealthCheckEntities()) {
                var dc = context.DomainControllerSet.FirstOrDefault();
                var proxy = dc as IEntityWithChangeTracker;
                Debug.WriteLine(proxy);
            }
        }

        [TestMethod]
        [TestCategory("TestRunner Initialization")]
        public void TestGetDCComputerAccounts() {
            List<ComputerProperties> dcComputerAccounts = StartupDataGenerator.GetDCComputerAccounts();
            Assert.IsTrue(dcComputerAccounts.Count > 0, "dcComputerAccounts.Count > 0 = False");

            Console.WriteLine("Domain Controller computer accounts for testing: {0}", dcComputerAccounts.Count);
            foreach (var dc in dcComputerAccounts) {
                Console.WriteLine("DC: {0}", dc.DN);
            }

            Console.WriteLine("{0} finished", Utility.CurrentMethodName());
        }

        [TestMethod]
        [TestCategory("TestRunner Initialization")]
        public void TestGetDomainGPOs() {
            Dictionary<string, List<Gpo>> domainGpos = StartupDataGenerator.GetDomainGPOs();

            Assert.IsTrue(domainGpos.Count > 0, "domainGpos.Count > 0 = False");
            Assert.IsTrue(domainGpos.All(x => x.Value.Count > 0), "domainGpos.All(x => x.Value.Count > 0) = False");

            foreach (var kvp in domainGpos) {
                Console.WriteLine("Domain: {0}", kvp.Key);
                foreach (var gpo in kvp.Value) {
                    Console.WriteLine(" - GPO: {0}", gpo.DisplayName);
                }
            }

            Console.WriteLine("{0} finished", Utility.CurrentMethodName());
        }

        [TestMethod]
        [TestCategory("TestRunner Initialization")]
        public void TestGetExcludedServers() {
            List<ExcludedServer> excludedServers = StartupDataGenerator.GetExcludedServers();
            Assert.IsTrue(excludedServers.Count > 0);

            Console.WriteLine("Excluded Servers:");

            foreach (var excludedServer in excludedServers) {
                if (!excludedServer.IsValid) continue;
                Console.WriteLine("Server:");
                Console.WriteLine(excludedServer.ToString());
            }

            Console.WriteLine("{0} finished", Utility.CurrentMethodName());
        }

        [TestMethod]
        [TestCategory("TestRunner Initialization")]
        public void TestGetFSMORoleOwners() {
            Dictionary<string, Dictionary<string, string>> fsmoRoleOwners = StartupDataGenerator.GetFSMORoleOwners();

            Assert.IsTrue(fsmoRoleOwners.Count > 0, "fsmoRoleOwners.Count > 0 = False");
            Assert.IsTrue(fsmoRoleOwners.All(x => x.Value.Count == 3), "fsmoRoleOwners.All(x => x.Value.Count == 3) = False");

            foreach (var kvp in fsmoRoleOwners) {
                Console.WriteLine("Domain: {0}", kvp.Key);
                foreach (var fsmRoleOwner in kvp.Value) {
                    Console.WriteLine(" - FSMO Role: {0} Owner: {1}", fsmRoleOwner.Key, fsmRoleOwner.Value);
                }
            }

            Console.WriteLine("{0} finished", Utility.CurrentMethodName());
        }

        [TestMethod]
        [TestCategory("TestRunner Initialization")]
        public void TestGetPDCSysvolTestFolders() {
            var domainPDCSysvolPolicyFolders = StartupDataGenerator.GetPDCSysvolTestFolders();
            Assert.IsTrue(domainPDCSysvolPolicyFolders.Count > 0, "domainPDCSysvolPolicyFolders.Count > 0 = False");
            Assert.IsTrue(domainPDCSysvolPolicyFolders.Values.All(x => x != null), "domainPDCSysvolPolicyFolders.Values.All(x => x != null) = False");

            foreach (var sysvolPolicyFolder in domainPDCSysvolPolicyFolders) {
                Console.WriteLine("Domain: {0} Policy Display Name: {1}; Folder Path: {2}",
                    sysvolPolicyFolder.Key, sysvolPolicyFolder.Value.DisplayName, sysvolPolicyFolder.Value.DirectoryPath);
            }

            Console.WriteLine("{0} finished", Utility.CurrentMethodName());
        }

        #endregion

        #region Complex Type Builders
        [TestMethod]
        [TestCategory("Complex Type Builders")]
        public void TestGroupPolicySysvolInfoBuilder() {
            Console.WriteLine(DateTime.Now);

            // Arrange
            var dcComputerAccount = GetDCComputerAccountForTest(TestDCName);
            var dc = GetADDomainControllerForTest(TestDCName);
            var siteServer = GetADSiteServerForTest(dc);
            var domainPDCSysvolPolicyFolders = StartupDataGenerator.GetPDCSysvolTestFolders();
            GroupPolicySysvolInfo pdcGroupPolicySysvolInfo = null;
            domainPDCSysvolPolicyFolders.TryGetValue(dcComputerAccount.DNSDomain, out pdcGroupPolicySysvolInfo);

            Assert.IsNotNull(pdcGroupPolicySysvolInfo);

            var domainController = GetDomainController(dcComputerAccount);
            var systemInfo = new DCHealthCheck.DomainModel.SystemInfo() {
                DetachedState = DetachedState.Added,
                DomainController = domainController
            };

            var systemInfoBuilder = new DCHealthCheck.SystemInfoBuilder(systemInfo, pdcGroupPolicySysvolInfo, siteServer);
            var systemInfoBuilderType = systemInfoBuilder.GetType();

            var getTimeSyncInfoMethod = systemInfoBuilderType.GetMethod("TestSysvol", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

            // Act
            getTimeSyncInfoMethod.Invoke(systemInfoBuilder, null);

            // Assert
            Assert.IsNotNull(systemInfo.GroupPolicySysvolInfo);

            var validationResults = systemInfo.GroupPolicySysvolInfo.Validate(null).ToList();
            if (validationResults.Where(x => x.MemberNames.Any(y => (y != "Id") && (y != "SystemInfo") && (y != "SystemInfoId"))).Count() > 0) {
                Console.WriteLine("Validation errors:");
                foreach (var result in validationResults) {
                    Console.WriteLine("-Property(s): {0} Validation Error Message: {1}",
                        result.MemberNames.ToList().ToDelimitedString(), result.ErrorMessage);
                }
            }

            Assert.IsTrue(validationResults.Where(x => x.MemberNames.Any(y => (y != "Id") && (y != "SystemInfo") && (y != "SystemInfoId"))).Count() == 0);

            Console.WriteLine(systemInfo.GroupPolicySysvolInfo.ToVerboseString());
            Console.WriteLine("{0} finished", DateTime.Now);

        }

        [TestMethod]
        [TestCategory("Complex Type Builders")]
        public void TestTimeSyncBuilder() {
            Console.WriteLine(DateTime.Now);

            // Arrange
            var dcComputerAccount = GetDCComputerAccountForTest(TestDCName);
            var dc = GetADDomainControllerForTest(TestDCName);
            var siteServer = GetADSiteServerForTest(dc);

            var domainController = GetDomainController(dcComputerAccount);
            var systemInfo = new DCHealthCheck.DomainModel.SystemInfo() {
                DetachedState = DetachedState.Added,
                DomainController = domainController
            };

            var systemInfoBuilder = new DCHealthCheck.SystemInfoBuilder(systemInfo, null, siteServer);
            var systemInfoBuilderType = systemInfoBuilder.GetType();

            var getTimeSyncInfoMethod = systemInfoBuilderType.GetMethod("GetTimeSyncInfo", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

            // Act
            getTimeSyncInfoMethod.Invoke(systemInfoBuilder, null);

            // Assert
            Assert.IsNotNull(systemInfo.TimeSyncInfo);

            var validationResults = systemInfo.TimeSyncInfo.Validate(null).ToList();
            if (validationResults.Where(x => x.MemberNames.Any(y => (y != "Id") && (y != "SystemInfo") && (y != "SystemInfoId"))).Count() > 0) {
                Console.WriteLine("Validation errors:");
                foreach (var result in validationResults) {
                    Console.WriteLine("-Property(s): {0} Validation Error Message: {1}",
                        result.MemberNames.ToList().ToDelimitedString(), result.ErrorMessage);
                }
            }

            Assert.IsTrue(validationResults.Where(x => x.MemberNames.Any(y => (y != "Id") && (y != "SystemInfo") && (y != "SystemInfoId"))).Count() == 0);

            Console.WriteLine(systemInfo.TimeSyncInfo.ToVerboseString());
            Console.WriteLine("{0} finished", DateTime.Now);

        }

        [TestMethod]
        [TestCategory("Complex Type Builders")]
        public void TestReplicationNeighborBuilder() {
            Console.WriteLine(DateTime.Now);

            // Arrange
            var dcComputerAccount = GetDCComputerAccountForTest(TestDCName);
            var dc = GetADDomainControllerForTest(TestDCName);
            var siteServer = GetADSiteServerForTest(dc);

            var domainController = GetDomainController(dcComputerAccount);
            var systemInfo = new DCHealthCheck.DomainModel.SystemInfo() {
                DetachedState = DetachedState.Added,
                DomainController = domainController
            };

            var systemInfoBuilder = new DCHealthCheck.SystemInfoBuilder(systemInfo, null, siteServer);
            var systemInfoBuilderType = systemInfoBuilder.GetType();

            var getDomainControllerInfoMethod = systemInfoBuilderType.GetMethod("GetDomainControllerInfo", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

            // Act
            getDomainControllerInfoMethod.Invoke(systemInfoBuilder, null);

            var getReplicationNeighborInfoMethod = systemInfoBuilderType.GetMethod("GetReplicationNeighborInfo", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            getReplicationNeighborInfoMethod.Invoke(systemInfoBuilder, null);

            // Assert
            Assert.IsTrue(systemInfo.ReplicationNeighbors.Count > 0, "replicationNeighbors.Count > 0 = false");
            Console.WriteLine("{0} finished", DateTime.Now);
        } 
        #endregion

    }
}
